﻿
using System;
using System.Collections.Generic;
using DotNetNeural.Utils.Adapters;
namespace DotNetNeural.Utils
{
    /// <summary>
    /// Provides set of methods for vector generation
    /// </summary>
    public class VectorGenerator
    {
        /// <summary>
        /// Generates random vector with given dimensions. Each component is between minValue and maxValue
        /// </summary>
        public float[] Generate(int dim, float minValue, float maxValue)
        {
            if (dim < 0)
                throw new ArgumentException("Illegal negative dimension");

            if (dim == 0)
                return new float[0];

            CryptoRandom rnd = new CryptoRandom();
            float[] vec = new float[dim];

            for (int i = 0; i < dim; ++i)
            {
                vec[i] = ((float)rnd.NextDouble(minValue, maxValue));
            }

            return vec;
        }

        /// <summary>
        /// Generates new vector based on source one. Each component of resulting vector will differ from source one by dispersion
        /// </summary>
        public float[] Generate(float[] source, float dispersion)
        {
            if (source == null)
                throw new NullReferenceException("Illegal null-reference source vector");

            if (dispersion < 0)
                throw new ArgumentException("Illegal negative dispersion");

            float[] result = new float[source.Length];
            CryptoRandom rnd = new CryptoRandom();

            for (int i = 0; i < source.Length; ++i)
            {
                bool sign = rnd.GenerateRandomSign();
                float salt = (float)rnd.NextDouble(0, dispersion);

                if (!sign)
                    salt = -salt;

                result[i] = (source[i] + salt);
            }

            return result;
        }

        /// <summary>
        /// Generates several float vectors with given dimensions. Each component is between minValue and maxValue.
        /// Distance between each pair of vectors is greater or equal given value
        /// </summary>
        public IEnumerable<float[]>  GenereateDistant(int count, float distance, int dim, float minValue, float maxValue)
        {
            if (count < 0 || distance < 0 || dim < 0)
                throw new ArgumentException("Illegal negative values");

            List<float[]> vectors = new List<float[]>() { Generate(dim, minValue, maxValue) };

            MetricAdapter metricsHelper = new MetricAdapter();

            for (int i = 0; i < count - 1; ++i)
            {
                float[] rndVec = Generate(dim, minValue, maxValue);
                bool flag = metricsHelper.ExceedsMaxDistance(rndVec, vectors, distance);

                while (!flag)
                {
                    rndVec = Generate(dim, minValue, maxValue);
                    flag = metricsHelper.ExceedsMaxDistance(rndVec, vectors, distance);
                }

                vectors.Add(rndVec);
            }

            return vectors.ToArray();
        }
    }
}
